//
// Created by suzhen on 18-1-16.
//

#pragma once

#include <hacks/base/hacks_conf.h>
#include <hacks/base/ev_fd.hpp>
#include <hacks/base/ev_utils.h>
#include <hacks/net/platform_port.h>
#include <hacks/net/ip_addr.h>

namespace hacks::net {

    class socket_ops {
    public:
        explicit socket_ops(ev_os_sock_t fd)
                : fd_(fd) {
        }

        int close() {
            return os_sock_close(fd_);
        }

        int shutdown_write() {
            return os_shutdown(fd_, SHUT_WR);
        }

        int set_keepalive(bool on) {
            int on_value = on ? 1 : 0;
            return os_setsockopt(fd_, SOL_SOCKET, SO_KEEPALIVE,
                                 &on_value, static_cast<socklen_t>(sizeof on_value));
        }

        int set_reuseport(bool on) {
#ifdef SO_REUSEPORT
            int on_value = on ? 1 : 0;
            return os_setsockopt(fd_, SOL_SOCKET, SO_REUSEPORT, &on_value,
                                 static_cast<socklen_t>(sizeof on_value));
#else
            return 0;
#endif
        }

        int set_reuseaddr(bool on) {
            int on_value = on ? 1 : 0;
            return os_setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR,
                                 &on_value, static_cast<socklen_t>(sizeof on_value));
        }

        int set_tcpnodelay(bool on) {
            int on_value = on ? 1 : 0;
            return os_setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY,
                                 &on_value, static_cast<socklen_t>(sizeof on_value));
        }

        //接受新的连接
        ev_os_sock_t do_accept() {
#if VALGRIND || defined (NO_ACCEPT4)
            ev_os_sock_t incoming_sock = ::accept(fd_, nullptr, nullptr);
            io_socket_ops ops(incoming_sock);
            ops.set_nonblock();
            ops.set_cloexec();
#elif  defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
            ev_os_sock_t incoming_sock = ::accept4(fd_, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC);
#endif

#ifdef PLATFORM_APPLE
            if (incoming_sock != INVALID_SOCKET) {
                int no_sig_pipe = 1;
                os_setsockopt(incoming_sock, SOL_SOCKET, SO_NOSIGPIPE, &no_sig_pipe, sizeof(int));
            }
#endif
            return incoming_sock;
        }

        int set_nonblock() {
            int flags = ::fcntl(fd_, F_GETFL, 0);
            flags |= O_NONBLOCK;
            return ::fcntl(fd_, F_SETFL, flags);
        }

        int set_cloexec() {
            int flags = ::fcntl(fd_, F_GETFL, 0);
            flags |= O_CLOEXEC;
            return ::fcntl(fd_, F_SETFL, flags);
        }

        //获取 sock_fd
        ev_os_sock_t get_sock_fd() const {
            return fd_;
        }

        //todo 优化绑定 v4 和 v6 的实现
        int bindv4(const ip4_addr &addr) {
            struct sockaddr_in inaddr = addr.get_sock_addr4();
            return ::bind(fd_, (struct sockaddr *) &inaddr, sizeof(struct sockaddr_in));
        }

        int bindv6(const ip6_addr &addr) {
            struct sockaddr_in6 inaddr = addr.get_sock_addr6();
            return ::bind(fd_, (struct sockaddr *) &inaddr, sizeof(struct sockaddr_in6));
        }

        int listen() {
            return ::listen(fd_, SOMAXCONN);
        }

    private:
        ev_os_sock_t fd_;
    };

}
